package websocket

import (
	"fmt"

	"github.com/gogf/gf/v2/container/garray"
	"github.com/gogf/gf/v2/container/gmap"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/util/gconv"

	"github.com/beherochuling/beesoft_api/app/helper/container"
)

var group = &groupService{
	groups: gmap.NewStrAnyMap(true),
}

type groupService struct {
	groups *gmap.StrAnyMap
}

func (r *groupService) Add(groupId string, client *Websocket) {
	r.groups.SetIfNotExist(groupId, gmap.New(true))
	clients, ok := r.groups.Get(groupId).(*gmap.Map)
	if ok {
		clients.Set(client.FD, client)
	}
}
func (r *groupService) Remove(groupId string, fd int64) {
	if r.groups.Contains(groupId) {
		clients, ok := r.groups.Get(groupId).(*gmap.Map)
		if ok {
			clients.Remove(fd)
			if clients.IsEmpty() { // 当房间内没有客户端时候，删掉房间
				r.groups.Remove(groupId)
			}
		}
	}
}

func (r *groupService) GroupExists(messageFrom int, groupId string) bool {
	if r.groups.Contains(groupId) {
		return true
	}

	if messageFrom == messageFromLocal {
		exists := false

		cluster.IterateWorkers(func(address string) bool {
			exists, _ = api.DoCheckExists(address, &clusterCheckExistsObject{
				Type:    checkExistTypeGroup,
				GroupId: groupId,
			})

			return exists
		})

		return exists
	}

	return false
}
func (r *groupService) UserExists(messageFrom int, groupId string, userId int) bool {
	if r.groups.Contains(groupId) {
		clients, ok := r.groups.Get(groupId).(*gmap.Map)
		if ok {
			exist := false

			clients.Iterator(func(fd interface{}, ws interface{}) bool {
				client, ok := ws.(*Websocket)
				if ok {
					if client.UserId == userId {
						exist = true

						return false // 终止迭代
					}
				}

				return true // 继续迭代
			})

			if exist {
				return true
			}
		}
	}

	if messageFrom == messageFromLocal {
		exists := false

		cluster.IterateWorkers(func(address string) bool {
			exists, _ = api.DoCheckExists(address, &clusterCheckExistsObject{
				Type:    checkExistTypeUser,
				GroupId: groupId,
				UserId:  userId,
			})

			return exists // true 终止迭代
		})

		return exists
	}

	return false
}

func (r *groupService) GetOne(groupId string, fd int64) *Websocket {
	if r.groups.Contains(groupId) {
		value, ok := r.groups.Get(groupId).(*gmap.Map)
		if ok {
			if ws, ok := value.Get(fd).(*Websocket); ok {
				return ws
			}
		}
	}

	return nil
}
func (r *groupService) GetAll(groupId string) []*Websocket {
	if r.groups.Contains(groupId) {
		var items []*Websocket

		value, ok := r.groups.Get(groupId).(*gmap.Map)
		if ok {
			value.Iterator(func(k interface{}, v interface{}) bool {
				if ws, ok := v.(*Websocket); ok {
					items = append(items, ws)
				}

				return true // 继续迭代
			})
		}

		return items
	}

	return nil
}

func (r *groupService) GetUsers(groupId string) []int {
	if groupId == "" {
		return []int{}
	}

	users := &garray.IntArray{}

	localUsers := r.GetLocalUsers(groupId)
	if localUsers != nil && !localUsers.IsEmpty() {
		users.Append(localUsers.Slice()...)
	}

	remoteUsers := r.GetRemoteUsers(groupId)
	if remoteUsers != nil && !remoteUsers.IsEmpty() {
		users.Append(remoteUsers.Slice()...)
	}

	return users.Unique().Slice()
}
func (r *groupService) GetLocalUsers(groupId string) *garray.IntArray {
	websockets := r.GetAll(groupId)
	if len(websockets) > 0 {
		localUsers := container.Collection.New(websockets).Pluck("UserId")
		return garray.NewIntArrayFrom(localUsers.Ints())
	}

	return nil
}
func (r *groupService) GetRemoteUsers(groupId string) *garray.IntArray {
	users := &garray.IntArray{}

	cluster.IterateWorkers(func(address string) bool {
		remoteUsers, err := api.DoGetUsers(address, groupId)
		if err != nil {
			glog.Error(ctx, err)
		}

		if len(remoteUsers) > 0 {
			users.Append(gconv.Ints(remoteUsers)...)
		}

		return false // 终止迭代
	})

	return users
}

func (r *groupService) CloseWebsocket(messageFrom int, groupId string, fd int64) {
	if fd <= 0 || groupId == "" {
		return
	}

	if r.groups.Contains(groupId) {
		clients, ok := r.groups.Get(groupId).(*gmap.Map)
		if ok {
			if clients.Contains(fd) {
				if ws, o := clients.Get(fd).(*Websocket); o {
					ws.close()
					r.Remove(groupId, fd)

					return
				}
			}
		}
	}

	if messageFrom == messageFromLocal {
		go func() { // 业务方不关心关闭的结果，因此无需同步调用，在协程里面执行即可
			data := &clusterCloseConnectionObject{
				GroupId: groupId,
				FD:      fd,
			}
			cluster.IterateWorkers(func(address string) bool {
				err := api.DoCloseConnection(address, data)
				if err != nil {
					glog.Error(ctx, fmt.Sprintf("failed to close websocket, address: %s, msg: %s, err: %v", address, gconv.String(data), err))
				}

				return false
			})
		}()
	}
}
func (r *groupService) SendMessage(messageFrom int, msg *clusterSendMessageObject, excludeFDs ...int64) {
	if msg.GroupId == "" || msg.Data == nil {
		return
	}

	groupId := msg.GroupId

	go func() {
		if r.groups.Contains(msg.GroupId) {
			value, ok := r.groups.Get(groupId).(*gmap.Map)
			if ok {
				value.Iterator(func(k interface{}, v interface{}) bool {
					if container.InArray(excludeFDs, gconv.Int64(k)) { // 排除自己
						return true
					}

					if ws, ok := v.(*Websocket); ok {
						if len(msg.Users) > 0 {
							if container.InArray(msg.Users, ws.UserId) {
								ws.Write(msg.Data)
							}
						} else { // 否则广播
							ws.Write(msg.Data)
						}
					}

					return true
				})
			}
		}
	}()

	if messageFrom == messageFromLocal {
		go func() {
			cluster.IterateWorkers(func(address string) bool {
				err := api.DoSendMessages(address, msg)
				if err != nil {
					glog.Error(ctx, err, fmt.Sprintf("address: %s, msg: %s", address, gconv.String(msg)))
				}

				return false // 继续迭代
			})
		}()
	}
}
